package com.meta.builder.freemarker.cls;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.meta.annotation.MetaGenAnnotation;
import com.meta.annotation.MetaMapper;
import com.meta.builder.IMetaTemplate;
import com.meta.builder.freemarker.field.ClassInfo;
import com.meta.builder.freemarker.field.FieldInfo;
import com.meta.builder.freemarker.tmp.ClassTypeTmpEnum;
import com.meta.internal.Options;
import freemarker.template.Template;

import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.meta.builder.freemarker.field.ClassInfo.IMPORTS_FORMAT;

public class ClassBuilder extends IMetaTemplate {

    private String defaultTmp = "package ${clsPackage};\n" +
            "\n<#list classInfo.imports as p>" +
            "${p}\n" +
            "</#list>\n" +
            "@Data\n" +
            "public class ${classInfo.clsName} {\n" +
            "\n<#if classInfo.fieldInfoList?? && (classInfo.fieldInfoList?size > 0)>" +
            "\n<#list classInfo.fieldInfoList as field >" +
            "<#if field.isJsonField>" +
            "@JSONField(name = \"${field.sourceField}\")</#if>\n" +
            "private ${field.fieldType} ${field.fieldName};\n" +
            "</#list>\n" +
            "</#if>\n" +
            "}\n";

    public String getClsPackage() {
        return clsPackage;
    }

    public void setClsPackage(String clsPackage) {
        this.clsPackage = clsPackage;
    }

    public ClassInfo getClassInfo() {
        return classInfo;
    }

    public void setClassInfo(ClassInfo classInfo) {
        this.classInfo = classInfo;
    }

    private boolean isClass(JSONObject object) {
        if (object.containsKey(options.getTypeTip())) {
            String objType = object.getString(options.getTypeTip());
            ClassTypeTmpEnum typeTmpEnum = ClassTypeTmpEnum.typeOf(objType);
            return ClassTypeTmpEnum.OBJECT.getCode().equals(typeTmpEnum.getCode());
        }
        return false;
    }

    private FieldInfo convertJsonToFields(JSONObject fieldJson, String fieldName) {
        FieldInfo fieldInfo = new FieldInfo();
        if (fieldJson.containsKey(options.getTypeTip())) {
            String objType = fieldJson.getString(options.getTypeTip());
            ClassTypeTmpEnum typeTmpEnum = ClassTypeTmpEnum.typeOf(objType);
            fieldInfo.setFieldType(typeTmpEnum.getCls());
            fieldInfo.setFieldName(StrUtil.lowerFirst(fieldName));
        } else if (fieldJson.containsKey("oneOf")) {
            fieldInfo.setFieldType(ClassTypeTmpEnum.STRING.getCls());
            fieldInfo.setFieldName(StrUtil.lowerFirst(fieldName));
        } else {
            return null;
        }
        return fieldInfo;
    }


    private void defaultClassInfo(MetaGenAnnotation[] metaAnnotations, ClassInfo classInfo) {
        for (MetaGenAnnotation metaAnnotation : metaAnnotations) {
            for (String anImport : metaAnnotation.imports()) {
                classInfo.getImports().add(String.format(IMPORTS_FORMAT, anImport));
            }
        }
    }

    @Override
    public Template getTemplate() {
        try {
            return options.getFkConfig().getTemplate(getTemplateNameForClass(ClassBuilder.class));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<ClassInfo> builder(JSONObject object, String baseName, Map<String, MetaMapper> mappers, MetaGenAnnotation[] metaAnnotations) {
        List<ClassInfo> classInfos = new ArrayList<>();

        //基类创建
        ClassInfo classInfo = new ClassInfo();
        classInfo.setClsName(baseName);

        List<FieldInfo> fieldInfoList = new ArrayList<>();
        List<String> imports = new ArrayList<>();
        imports.addAll(Options.defaultImports);
        classInfo.setFieldInfoList(fieldInfoList);
        classInfo.setImports(imports);

        defaultClassInfo(metaAnnotations, classInfo);

        //解析类型
        if (isClass(object)) {
            if (object.containsKey(options.getPropertiesTip()) || object.containsKey("patternProperties")) {
                JSONObject proJsonObj = object.getJSONObject(options.getPropertiesTip());
                if (proJsonObj == null) {
                    proJsonObj = object.getJSONObject("patternProperties");
                }
                //解析属性
                for (Map.Entry<String, Object> value : proJsonObj.entrySet()) {
                    if (value.getValue() instanceof JSONObject) {
                        JSONObject valueObj = (JSONObject) value.getValue();
                        String key = convertByMappers(value.getKey(), mappers);
                        if (isClass(valueObj)) {
                            FieldInfo fieldInfo = new FieldInfo();
                            fieldInfo.setFieldType(StrUtil.upperFirst(key) + IMPLEMENTATION_SUFFIX);
                            fieldInfo.setSourceField(value.getKey());
                            fieldInfo.setFieldName(StrUtil.lowerFirst(key));
                            imports.add(String.format(IMPORTS_FORMAT, this.clsPackage + "." + fieldInfo.getFieldType()));
                            fieldInfoList.add(fieldInfo);
                            classInfos.addAll(builder((JSONObject) value.getValue(), StrUtil.upperFirst(key) + "DTO", mappers, metaAnnotations));
                            options.getProcessingEnv().getMessager().printMessage(Diagnostic.Kind.WARNING, "======doing fieldInfo:" + value.getKey());
                            //导入对象设置
                            //对象属性设置
                        } else {
                            FieldInfo fieldInfo = convertJsonToFields(valueObj, key);
                            if (fieldInfo != null) {
                                fieldInfo.setSourceField(value.getKey());
                                fieldInfoList.add(fieldInfo);
                            } else {
                                options.getProcessingEnv().getMessager().printMessage(Diagnostic.Kind.WARNING, "======doing fieldType not exists" + value.getKey());
                            }
                        }

                    }
                }
            }
            classInfos.add(classInfo);
        }

        return classInfos;
    }

    /**
     * key值转换
     *
     * @param key
     * @param mappers
     * @return
     */
    private String convertByMappers(String key, Map<String, MetaMapper> mappers) {
        if (mappers.containsKey(key)) {
            return mappers.get(key).target();
        }
        return key;
    }
}
